﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Security;
using System.Security.Cryptography;
using System.Diagnostics;
using System.IO;

namespace Encryption
{
    public class Security
    {
        // these three variables need to match the values in Security.java
        string GLOBAL_ENCRYPTION_PASSWORD = "SECURITY_KEY";
        string SALT = "default_salt";        
        sbyte[] siv = new sbyte[]{
            (sbyte)-8  , (sbyte)-7, (sbyte)-6, (sbyte)-5 , (sbyte)-4 , (sbyte)-3 , (sbyte)-2  , (sbyte)-1,
            (sbyte)1, (sbyte)2  , (sbyte)3, (sbyte)4, (sbyte)5, (sbyte)6 , (sbyte)7, (sbyte)8
        };
        //
        
        
        public bool UseBase64 = true;

        // enum to determine which algorithm to use
        // MD5 -> MD5CryptoServiceProvider (128 bit)
        // SHA256 -> SHA256CryptoServiceProvider (256 bit)
        public enum EncryptionType
        {
            MD5, SHA256
        }

        /// <summary>
        /// Construtor requires password to use these methods
        /// </summary>        
        public Security()
        {            

        }

        /// <summary>
        /// Construtor requires password to use these methods.  
        /// Base64 is default, set to false to use Base16
        /// </summary>        
        /// <param name="UseBase64"></param>
        public Security(bool UseBase64)
        {
            this.UseBase64 = UseBase64;
        }        

        public void SetPassword(string password)
        {
            GLOBAL_ENCRYPTION_PASSWORD = password;
        }

        public void SetSalt(string salt)
        {
            SALT = salt;
        }

        public string EncryptWithSalt(string src, string salt, EncryptionType type)
        {
            ICryptoTransform transform = GetCrypto(type, true, salt);
            byte[] data = Encoding.UTF8.GetBytes(src);
            byte[] encryptedData = transform.TransformFinalBlock(data, 0, data.Length);
            if (UseBase64)
                return Convert.ToBase64String(encryptedData);
            else
                return ByteToHex(encryptedData);
        }

        public byte[] Encrypt(byte[] data, string password, EncryptionType type)
        {
            ICryptoTransform transform = GetCrypto(type, true, "");            
            byte[] encryptedData = transform.TransformFinalBlock(data, 0, data.Length);
            return encryptedData;
        }

        public byte[] Decrypt(byte[] encryptedData, string password, EncryptionType type)
        {       
            ICryptoTransform transform = GetCrypto(type, false, "");
            return transform.TransformFinalBlock(encryptedData, 0, encryptedData.Length);            
        }

        public string Encrypt(string src, EncryptionType type)
        {
            return EncryptWithSalt(src, "", type);
        }
       
        public string Decrypt(string src, EncryptionType type)
        {
            return DecryptWithSalt(src, "", type);
        }

        public string DecryptWithSalt(string src, string salt, EncryptionType type)
        {
            byte[] encryptedData = null;
            if (UseBase64)
                encryptedData = Convert.FromBase64String(src);
            else
                encryptedData = HexToByte(src);

            ICryptoTransform transform = GetCrypto(type, false, salt);
            byte[] decoded = transform.TransformFinalBlock(encryptedData, 0, encryptedData.Length);
            return Encoding.UTF8.GetString(decoded);
        }

        public byte[] DecryptContent(byte[] encryptedData, EncryptionType type, string customer, byte[] password)
        {
            ICryptoTransform transform = GetCrypto(type, false, customer, password);            
            return transform.TransformFinalBlock(encryptedData, 0, encryptedData.Length);
        }

        private ICryptoTransform GetCrypto(EncryptionType type, bool encrypt, string salt, byte[] password)
        {
            byte[] iv = new byte[siv.Length];
            for (int i = 0; i < iv.Length; i++)
                iv[i] = (byte)siv[i];

            RijndaelManaged cipher = new RijndaelManaged();
            cipher.Mode = CipherMode.CBC;
            cipher.Padding = PaddingMode.ISO10126;

            if (type == EncryptionType.SHA256)
            {
                cipher.KeySize = 256;
                cipher.BlockSize = 128;
            }
            else
            {
                cipher.KeySize = 128;
                cipher.BlockSize = 128;
            }
            byte[] key = HashPasswordBytes(password, type);
            if (salt.Length > 0)
            {
                key = HashPasswordBytes(password, salt, type);
            }
            cipher.Key = key;
            cipher.IV = iv;

            if (encrypt)
                return cipher.CreateEncryptor();
            else
                return cipher.CreateDecryptor();
        }

        private ICryptoTransform GetCrypto(EncryptionType type, bool encrypt, string salt)
        {
            return GetCrypto(type, encrypt, salt, HashPasswordBytes(GLOBAL_ENCRYPTION_PASSWORD, SALT, type));
        }

        private ICryptoTransform GetCrypto(EncryptionType type, bool encrypt)
        {
            return GetCrypto(type, encrypt, "");
        }

        private byte[] HashPasswordBytes(byte[] src, string salt, EncryptionType type)
        {
            HashAlgorithm alg;
            if (type == EncryptionType.MD5)
                alg = new MD5CryptoServiceProvider();
            else
                alg = new SHA256CryptoServiceProvider();

            Encoding encoding = System.Text.UTF8Encoding.UTF8;            
            alg.TransformBlock(src, 0, src.Length, src, 0);
            if (salt.Length > 0)
            {
                byte[] bsalt = encoding.GetBytes(salt);
                alg.TransformBlock(bsalt, 0, bsalt.Length, bsalt, 0);
            }
            alg.TransformFinalBlock(new byte[0], 0, 0);
            return alg.Hash;
        }

        private byte[] HashPasswordBytes(byte[] src, EncryptionType type)
        {
            return HashPasswordBytes(src, "", type);
        }

        private byte[] HashPasswordBytes(string src, string salt, EncryptionType type)
        {
            Encoding encoding = System.Text.UTF8Encoding.UTF8;
            byte[] data = encoding.GetBytes(src);
            return HashPasswordBytes(data, salt, type);            
        }

        private byte[] HexToByte(string src)
        {            
            byte[] bytes = new byte[src.Length / 2];
            for (int i = 0; i < src.Length; i += 2)
                bytes[i / 2] = Convert.ToByte(src.Substring(i, 2), 16);
            return bytes;
		}	
				
        private string ByteToHex(byte[] ByteArray)
        {
            StringBuilder sb = new StringBuilder();

            if ((ByteArray != null) && (ByteArray.Length > 0))
            {
                for (int i = 0; i < ByteArray.Length; i++)
                {
                    sb.Append(ByteArray[i].ToString("X2"));
                }
            }
            return sb.ToString();
        }
        
        public string EncodeToBase64(string src)
        {
            byte[] toEncodeAsBytes = null;            
            toEncodeAsBytes = System.Text.ASCIIEncoding.ASCII.GetBytes(src);            
            return System.Convert.ToBase64String(toEncodeAsBytes);
        }

        public string DecodeFromBase64(string src)
        {
            byte[] data = System.Convert.FromBase64String(src);
            return System.Text.ASCIIEncoding.ASCII.GetString(data);                        
        }
                
        public byte[] DecodeBytesFromBase64(string src)
        {
            return System.Convert.FromBase64String(src);            
        }

        public string HashPassword(string src, EncryptionType type)
        {            
            if (UseBase64)
                return Convert.ToBase64String(HashPasswordBytes(src, SALT, type));
            else
                return ByteToHex(HashPasswordBytes(src, SALT, type));        
        }

        public string Hash(byte[] data, EncryptionType type)
        {            
            byte[] hash = null;

            if (type == EncryptionType.MD5)
            {                
                MD5CryptoServiceProvider MD5 = new MD5CryptoServiceProvider();
                hash = MD5.ComputeHash(data);                
            }
            else if (type == EncryptionType.SHA256)
            {                
                SHA256CryptoServiceProvider MD5 = new SHA256CryptoServiceProvider();
                hash = MD5.ComputeHash(data);                
            }
            else
                return "";

            if (UseBase64)
                return Convert.ToBase64String(hash);
            else
                return ByteToHex(hash);
        }

        public string Hash(string src, EncryptionType type)
        {            
            Encoding encoding = System.Text.UTF8Encoding.UTF8;
            byte[] data = encoding.GetBytes(src);
            return Hash(data, type);

        }
    }
}
